package com.movie.ticket.sale.moviezuul.filter;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.movie.ticket.sale.moviecommon.common.Result;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.data.redis.core.BoundValueOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.List;

import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.RIBBON_ROUTING_FILTER_ORDER;


/**
 * @BelongsProject: gateway
 * @BelongsPackage:
 * @Author: lyl
 * @CreateTime: 2020-11-19 16:04
 * @Description: 验证token
 */
@Component
@Slf4j
@RefreshScope
public class AuthFilter extends ZuulFilter {
    private static final List<String> EXXLUDED = Arrays.asList("/author", "/ticket");//放行--范围
    private static final List<String> INCLUDE = Arrays.asList("/author/logout", "/ticket/set/query");//不放行--指定url

    //配置文件中的数据
    @Value("${myUrls.myExclude}")   //放行
    private String exclude;
    @Value("${myUrls.myInclude}")   //不放行
    private String include;


    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Override
    public String filterType() {

        return PRE_TYPE;//pre类型 在路由之前 认证 鉴权
    }

    @Override
    public int filterOrder() {
        return RIBBON_ROUTING_FILTER_ORDER - 5;
    }

    @Override
    public boolean shouldFilter() {
        //放行登录 注册 登出
        RequestContext context = RequestContext.getCurrentContext();
        String requestURI = context.getRequest().getRequestURI();
        log.info("------ 验证token，request.uri： {}", requestURI);
        String[] exA = exclude.split(",");
        String[] inA = include.split(",");
        log.info("放行：{}", Arrays.deepToString(exA));
        log.info("不放行：{}", Arrays.deepToString(inA));
        for (String s : inA) {
            if (requestURI.equals(s)) {
                log.info("1需要验证token");
                return true;
            }
        }
        for (String s : exA) {
            if (requestURI.contains(s)) {
                log.info("2不验证token");
                return false;
            }
        }
        log.info("3需要验证token");
        return true;
//        return !EXXLUDED.contains(requestURI);
    }

    @Override
    public Object run() throws ZuulException {
        log.info("进入：验证token");
        //其他的请求进来的做法
        RequestContext context = RequestContext.getCurrentContext();
        HttpServletRequest request = context.getRequest();
        String requestURI = request.getRequestURI();
        //要求每次请求，前端在请求中放入 pass + {token}
        //获取到token 放在请求头中
        String authorization = request.getHeader("Authorization");
        //如果token 为空  直接失败
        if (StringUtils.isBlank(authorization) || !authorization.contains("pass")) {
            log.info("token为空");
            responseError(context, "token为空");
            return null;
        }
        //token不为空
        String token = authorization.replace("pass", "");
        Claims claims = validateToken(token);
        //校验token
        if (claims == null) {
            log.info("token校验失败");
            responseError(context, "token校验失败");
            return null;
        }

        context.setSendZuulResponse(true);
        //生成解析对象 存入RequestHeader

        Object id = claims.get("id");
        Object role = claims.get("role");
        Object tel = claims.get("tel");

//        System.out.println(id);
//        System.out.println(role);
//        System.out.println(tel);

        String key = "manager." + role + "." + id;
        BoundValueOperations<String, String> rs = redisTemplate.boundValueOps(key);

        String value = rs.get();
        if (null == value) {
            log.info("redis缓存失效，请重新登录");
            responseError(context, "redis缓存失效，请重新登录");
            return null;
        }

        String oldToken = value.split("-:-")[1];
        if (!requestURI.equals("/author/logout"))
            if (!oldToken.equals(token)) {
                log.info("token已过期，请传入最新token");
                responseError(context, "token已过期，请传入最新token");
                return null;
            }

        context.addZuulRequestHeader("tel", "" + tel);
        context.addZuulRequestHeader("id", "" + id);
        context.addZuulRequestHeader("role", "" + role);
        context.setSendZuulResponse(true);

//            request.setAttribute();
//            HttpServletResponse response = context.getResponse();
//            response.addHeader("tel",""+tel);
//            response.addHeader("id",""+id);
//            response.addHeader("role",""+role);
//            System.out.println(token);

        return null;
    }

    private void responseError(RequestContext cxt, String message) {
        //直接返回结果
        cxt.setSendZuulResponse(false);
        cxt.getResponse().setContentType("application/json;charset=utf-8");
        cxt.setResponseStatusCode(HttpStatus.SC_CONFLICT);

        try {
            cxt.setResponseBody(objectMapper.writeValueAsString(Result.fail(405, message)));
        } catch (JsonProcessingException e) {
            ReflectionUtils.rethrowRuntimeException(e);
        }
    }

    /**
     * 校验token
     *
     * @param token
     * @return
     */
    private Claims validateToken(String token) {
        try {
            Claims claims = JWTHelper.parseJWT(token);
            return claims;
        } catch (JwtException e) {
            log.info("JWT解析失败", e);
            return null;
        }
    }

}
